// Copyright 2011, Vanya Davidenko.
// Кодировка файла: UTF-8. 
package info.iu9.red;



import java.util.HashMap;

class FiniteAutomataAnalyzer {
   private final class Visitor implements StateVisitor {
    public void visitAll() {
      for ( final State s : fa_ ) {
        s.accept(this);
      }
    }

    public void visit(final MatchState s) {
    }
    public void visit(final JumpState s) {
    }
    public void visit(final ForkState s) {
    }
    public void visit(final BeginSaveState s) {
      // (a){5} будет 5 Save1 состояний, но реально это одно.
      num_submatches_ = Math.max(s.submatch_num(), num_submatches_);
    }
    public void visit(final EndSaveState s) {
    }
    public void visit(final BeginningAnchorState s) {
    }
    public void visit(final EndOfStringAnchorState s) {
    }
    public void visit(final EndingAnchorState s) {
    }
    public void visit(final ReverseEndingAnchorState s) {
    }
    public void visit(final StringBeginningAnchorState s) {
    }
    public void visit(final StringEndingAnchorState s) {
    }
    public void visit(final MultiLineBeginningAnchorState s) {
    }
    public void visit(final MultiLineEndingAnchorState s) {
    }
    public void visit(final WordBoundaryAnchorState s) {
    }
    public void visit(final InvertedWordBoundaryAnchorState s) {
    }
    public void visit(final DotState s) {
      final int[] c = new int[1];
      c[0] = '\n';
      doCharFact(c, interval_num_++);
    }
    public void visit(final DotAllState s) {
    }
    public void visit(final CharacterState s) {
      doCharFact(s.allCharacters(), interval_num_++);
    }
    public void visit(final CaseCharacterState s) {
      doCharFact(s.allCharacters(), interval_num_++);
    }
    public void visit(final CharacterGroupState s) {
      doCharFact(s.allCharacters(), interval_num_++);
    }
    public void visit(final CaseCharacterGroupState s) {
      doCharFact(s.allCharacters(), interval_num_++);
    }

    private void doCharFact(final int[] characters, final int interval_num) {
      assert interval_num < 64;
      for ( int i = 0 ; i != characters.length ; i++ ) {
        temp_fact_map_[i] |= 1L << interval_num;
      }
    }

    private int interval_num_ = 1;
    private static final int level = 0x10FFFF + 1;
  }

  FiniteAutomataAnalyzer(final FiniteAutomata fa) {
    fa_ = fa;
    temp_fact_map_ = new long[0x10FFFF + 1];
    fact_map_ = new int[0x10FFFF + 1];

    new Visitor().visitAll();
    compressFactMap();
    num_submatches_ += 1;  // число больше максимального номера на 1.
  }

  int num_submatches() {
    return num_submatches_;
  }

  boolean dfa_is_enough() {
    return dfa_is_enough_;
  }

  int[] fact_map() {
    return fact_map_;
  }

  int max_fact_num() {
    return max_fact_num_;
  }


  private void compressFactMap() {
    int current_index = 1;
    final HashMap<Long, Integer> map = new HashMap<Long, Integer>();
    for ( int i = 0 ; i != temp_fact_map_.length ; i++) {
      Integer g = null;
      if ( temp_fact_map_[i] == 0 ) {  // уменьшаем число обращение к map
        continue;
        //fact_map_[i] = 0;
      } else if ( ( g = map.get(temp_fact_map_[i]) ) == null ) {
        map.put(temp_fact_map_[i], current_index);
        fact_map_[i] = current_index;
        current_index++;
        assert current_index > 0;
      } else {
        fact_map_[i] = g;
      }
    }
    max_fact_num_ = current_index;
    assert current_index > 0;
  }

  private final FiniteAutomata fa_;
  private final long[] temp_fact_map_;
  private final int[] fact_map_;
  private int max_fact_num_;

  private int num_submatches_;
  private boolean dfa_is_enough_;
}
